package cn.master.volley.commons;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.net.http.AndroidHttpClient;
import android.os.Build;
import android.os.Environment;
import android.support.v4.util.LruCache;

import com.android.volley.AuthFailureError;
import com.android.volley.Cache;
import com.android.volley.Cache.Entry;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Network;
import com.android.volley.Request.Method;
import com.android.volley.Request.Priority;
import com.android.volley.RequestQueue;
import com.android.volley.Response.ErrorListener;
import com.android.volley.toolbox.BasicNetwork;
import com.android.volley.toolbox.DiskBasedCache;
import com.android.volley.toolbox.HttpClientStack;
import com.android.volley.toolbox.HttpStack;
import com.android.volley.toolbox.HurlStack;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.ImageLoader.ImageCache;

import org.apache.http.protocol.HTTP;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import cn.master.util.FileSizeUtil;
import cn.master.volley.commons.CustomHeaderRequest.IsCacheListener;

/**
 * Volley 网络请求类
 */
public class VolleyHelper {

    public static final String TAG = VolleyHelper.class.getSimpleName();
    /**
     * 网络超时15秒
     */
    private static final int SOCKET_TIMEOUT = 15000;
    /**
     * 如果请求失败后可以重试，最大重试次数
     */
    private static final int MAX_RETRIES = 5;
    /**
     * 数据的最大缓存（10M）
     */
    private static final int DATA_MAX_CACHE_SIZE = 10 * 1024 * 1024;
    /**
     * 图片L1 （内存）的最大缓存（10M）
     */
    private static final int IMAGE_L1_MAX_CACHE_SIZE = 10 * 1024 * 1024;
    /**
     * 图片L2（磁盘）的最大缓存（20M）
     */
    private static final int IMAGE_L2_MAX_CACHE_SIZE = 20 * 1024 * 1024;
    /**
     * 默认的磁盘缓存文件夹名
     */
    private static final String DEFAULT_CACHE_DIR = "volley";
    /**
     * 数据缓存的文件夹名
     */
    private static final String DATA_CACHE_DIR = "volley_data";
    /**
     * 图片缓存的文件夹名
     */
    private static final String IMAGE_CACHE_DIR = "volley_img";
    /**
     * 应用中数据的请求队列
     */
    private static RequestQueue REQUEST_QUEUE_DATA = null;
    /**
     * 图片加载对象
     */
    private static ImageLoader IMAGE_LOADER = null;

    public static void init(Context context) {
        if (REQUEST_QUEUE_DATA == null) {
            REQUEST_QUEUE_DATA = newRequestQueue(context);
        }
        if (IMAGE_LOADER == null) {
            IMAGE_LOADER = newImageLoader(context);
        }

    }

    /**
     * 创建一个数据请求的队列
     *
     * @param context
     */
    private static RequestQueue newRequestQueue(Context context) {
        File cacheDir = new File(context.getCacheDir(), DATA_CACHE_DIR);
        DiskBasedCache diskBasedCache = new DiskBasedCache(cacheDir, DATA_MAX_CACHE_SIZE);
        RequestQueue requestQueue = newRequestQueue(diskBasedCache, null, context);
        return requestQueue;
    }

    /**
     * 创建一个ImageLoader对象
     *
     * @param context
     */
    private static ImageLoader newImageLoader(Context context) {
        BitmapLruCache bitmapLruCache = new BitmapLruCache(IMAGE_L1_MAX_CACHE_SIZE);
        File cacheDir = new File(context.getCacheDir(), IMAGE_CACHE_DIR);
        DiskBasedCache diskBasedCache = new DiskBasedCache(cacheDir, IMAGE_L2_MAX_CACHE_SIZE);
        RequestQueue requestQueue = newRequestQueue(diskBasedCache, null, context);
        return new ImageLoader(requestQueue, bitmapLruCache);
    }

    /**
     * Post 请求
     *
     * @param method        请求方式 get post put delete
     * @param url           请求地址
     * @param tag           给请求添加标签，用于取消请求
     * @param headers       设置请求头 ，如果为Null，则请求将使用默认配置
     * @param priority      请求优先级	 LOW(低),NORMAL(默认，正常的),HIGH(高),IMMEDIATE(直接的)
     * @param isCache       是否缓存
     * @param isRetry       是否开启重试策略，默认不开启
     * @param params        请求参数 ， 如果为null，则不添加任何参数
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     */
    private static void request(int method,
                                String url,
                                Object tag,
                                final HashMap<String, String> headers,
                                final Priority priority,
                                boolean isCache,
                                boolean isRetry,
                                final HashMap<String, String> params,
                                IsCacheListener<String> listener,
                                ErrorListener errorListener) {
        //创建一个请求
        CustomHeaderRequest request = new CustomHeaderRequest(method, url, listener,
                errorListener) {
            //添加请求参数
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                if (params == null || params.isEmpty()) {
                    return super.getParams();
                }
                return params;
            }

            /**
             * 添加请求头信息
             * @param context
             */
            @Override
            public Map<String, String> getHeaders(Context context) throws AuthFailureError {
                //添加API验证头信息
                if (headers == null || headers.isEmpty()) {
                    return super.getHeaders(context);
                }
                super.getHeaders(context).putAll(headers);
                return super.getHeaders(context);
            }

            //设置请求优先级
            @Override
            public Priority getPriority() {
                if (priority == null)
                    return super.getPriority();
                return priority;
            }
        };

        //为请求添加Tag
        if (tag != null)
            request.setTag(tag);
        //是否开启缓存
        request.setShouldCache(isCache);
        //设置重试策略
        if (isRetry) {
            request.setRetryPolicy(new DefaultRetryPolicy(SOCKET_TIMEOUT, MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
        } else {
            request.setRetryPolicy(new DefaultRetryPolicy(SOCKET_TIMEOUT, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
        }
        REQUEST_QUEUE_DATA.add(request);
    }

    /**
     * Post 请求
     *
     * @param url           请求地址
     * @param tag           给请求添加标签，用于取消请求
     * @param headers       设置请求头 ，如果为Null，则请求将使用默认配置
     * @param priority      请求优先级	 LOW(低),NORMAL(默认，正常的),HIGH(高),IMMEDIATE(直接的)
     * @param isRetry       是否开启重试策略，默认不开启
     * @param params        请求参数 ， 如果为null，则不添加任何参数
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)} .
     */
    public static void post(String url,
                            Object tag,
                            final HashMap<String, String> headers,
                            final Priority priority,
                            boolean isRetry,
                            final HashMap<String, String> params,
                            IsCacheListener<String> listener,
                            ErrorListener errorListener) {
        request(Method.POST, url, tag, headers, priority, false, isRetry, params, listener, errorListener);
    }

    /**
     * 可添加 HTTP header，添加TAG， 优先级普通，失败后不重试
     *
     * @param url           请求地址
     * @param tag           给请求添加标签，用于取消请求
     * @param headers       设置请求头
     * @param params        请求参数
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)} .
     */
    public static void post(String url, Object tag, HashMap<String, String> headers,
                            final HashMap<String, String> params, IsCacheListener<String> listener,
                            ErrorListener errorListener) {
        request(Method.POST, url, tag, headers, null, false, false, params, listener, errorListener);
    }

    /**
     * 可添加TAG ，优先级普通，失败后不重试
     *
     * @param url           请求地址
     * @param tag           给请求添加标签，用于取消请求
     * @param params        请求参数
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)} .
     */
    public static void post(String url, Object tag,
                            final HashMap<String, String> params, IsCacheListener<String> listener,
                            ErrorListener errorListener) {
        request(Method.POST, url, tag, null, null, false, false, params, listener, errorListener);
    }

    /**
     * 优先级普通，失败后不重试
     *
     * @param url           请求地址
     * @param params        请求参数
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)} .
     */
    public static void post(String url,
                            final HashMap<String, String> params, IsCacheListener<String> listener,
                            ErrorListener errorListener) {
        request(Method.POST, url, null, null, null, false, false, params, listener, errorListener);
    }

    /**
     * Put 请求
     *
     * @param url           请求地址
     * @param tag           给请求添加标签，用于取消请求
     * @param headers       设置请求头 ，如果为Null，则请求将使用默认配置
     * @param priority      请求优先级	 LOW(低),NORMAL(默认，正常的),HIGH(高),IMMEDIATE(直接的)
     * @param isRetry       是否开启重试策略，默认不开启
     * @param params        请求参数 ， 如果为null，则不添加任何参数
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)} .
     */
    public static void put(String url,
                           Object tag,
                           final HashMap<String, String> headers,
                           final Priority priority,
                           boolean isRetry,
                           final HashMap<String, String> params,
                           IsCacheListener<String> listener,
                           ErrorListener errorListener) {
        request(Method.PUT, url, tag, headers, priority, false, isRetry, params, listener, errorListener);
    }

    /**
     * 可添加 HTTP header，添加TAG， 优先级普通，失败后不重试
     *
     * @param url           请求地址
     * @param tag           给请求添加标签，用于取消请求
     * @param headers       设置请求头
     * @param params        请求参数
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)} .
     */
    public static void put(String url, Object tag, HashMap<String, String> headers,
                           final HashMap<String, String> params, IsCacheListener<String> listener,
                           ErrorListener errorListener) {
        request(Method.PUT, url, tag, headers, null, false, false, params, listener, errorListener);
    }

    /**
     * 可添加TAG ，优先级普通，失败后不重试
     *
     * @param url           请求地址
     * @param tag           给请求添加标签，用于取消请求
     * @param params        请求参数
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)} .
     */
    public static void put(String url, Object tag,
                           final HashMap<String, String> params, IsCacheListener<String> listener,
                           ErrorListener errorListener) {
        request(Method.PUT, url, tag, null, null, false, false, params, listener, errorListener);
    }

    /**
     * 优先级普通，失败后不重试
     *
     * @param url           请求地址
     * @param params        请求参数
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)} .
     */
    public static void put(String url,
                           final HashMap<String, String> params, IsCacheListener<String> listener,
                           ErrorListener errorListener) {
        request(Method.PUT, url, null, null, null, false, false, params, listener, errorListener);
    }

    /**
     * Get 请求
     *
     * @param url           请求地址
     * @param tag           给请求添加标签，用于取消请求
     * @param headers       设置请求头 ，如果为Null，则请求将使用默认配置
     * @param priority      请求优先级	 LOW(低),NORMAL(默认，正常的),HIGH(高),IMMEDIATE(直接的)
     * @param isCache       是否缓存
     * @param isRetry       是否开启重试策略，默认不开启
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)}}}
     */
    public static void get(String url, Object tag, final HashMap<String, String> headers, final Priority priority,
                           boolean isCache, boolean isRetry,
                           IsCacheListener<String> listener,
                           ErrorListener errorListener) {
        request(Method.GET, url, tag, headers, priority, isCache, isRetry, null, listener, errorListener);
    }

    /**
     * 可添加 HTTP header，添加TAG， 优先级普通，失败后不重试
     *
     * @param url           请求地址
     * @param tag           给请求添加标签，用于取消请求
     * @param headers       设置请求头
     * @param isCache       是否缓存
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)} .
     */
    public static void get(String url, Object tag, HashMap<String, String> headers, boolean isCache,
                           IsCacheListener<String> listener,
                           ErrorListener errorListener) {
        request(Method.GET, url, tag, headers, null, isCache, false, null, listener, errorListener);
    }

    /**
     * 可添加TAG ，优先级普通，失败后不重试
     *
     * @param url           请求地址
     * @param tag           给请求添加标签，用于取消请求
     * @param isCache       是否缓存
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)} .
     */
    public static void get(String url, Object tag, boolean isCache,
                           IsCacheListener<String> listener,
                           ErrorListener errorListener) {
        request(Method.GET, url, tag, null, null, isCache, false, null, listener, errorListener);
    }

    /**
     * 优先级普通，失败后不重试
     *
     * @param url           请求地址
     * @param isCache       是否缓存
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)} .
     */
    public static void get(String url, boolean isCache,
                           IsCacheListener<String> listener,
                           ErrorListener errorListener) {
        request(Method.GET, url, null, null, null, isCache, false, null, listener, errorListener);
    }

    /**
     * Delete 请求
     *
     * @param url           请求地址
     * @param tag           给请求添加标签，用于取消请求
     * @param headers       设置请求头 ，如果为Null，则请求将使用默认配置
     * @param priority      请求优先级	 LOW(低),NORMAL(默认，正常的),HIGH(高),IMMEDIATE(直接的)
     * @param isRetry       是否开启重试策略，默认不开启
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)}
     */
    public static void delete(String url, Object tag, final HashMap<String, String> headers, final Priority priority, boolean isRetry,
                              IsCacheListener<String> listener,
                              ErrorListener errorListener) {
        request(Method.DELETE, url, tag, headers, priority, false, isRetry, null, listener, errorListener);
    }

    /**
     * 可添加 HTTP header，添加TAG， 优先级普通，失败后不重试
     *
     * @param url           请求地址
     * @param tag           给请求添加标签，用于取消请求
     * @param headers       设置请求头
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)} .
     */
    public static void delete(String url, Object tag, HashMap<String, String> headers,
                              IsCacheListener<String> listener,
                              ErrorListener errorListener) {
        request(Method.DELETE, url, tag, headers, null, false, false, null, listener, errorListener);
    }

    /**
     * 可添加TAG ，优先级普通，失败后不重试
     *
     * @param url           请求地址
     * @param tag           给请求添加标签，用于取消请求
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)} .
     */
    public static void delete(String url, Object tag,
                              IsCacheListener<String> listener,
                              ErrorListener errorListener) {
        request(Method.DELETE, url, tag, null, null, false, false, null, listener, errorListener);
    }

    /**
     * 优先级普通，失败后不重试
     *
     * @param url           请求地址
     * @param listener      请求成功监听器
     * @param errorListener 请求失败监听器
     * @see {@link #request(int, String, Object, HashMap, Priority, boolean, boolean, HashMap, IsCacheListener, ErrorListener)} .
     */
    public static void delete(String url,
                              IsCacheListener<String> listener,
                              ErrorListener errorListener) {
        request(Method.DELETE, url, null, null, null, false, false, null, listener, errorListener);
    }

    /**
     * 取消请求
     *
     * @param tag 请求的标签
     */
    public static void cancelRequest(Object tag) {
        REQUEST_QUEUE_DATA.cancelAll(tag);
    }

    /**
     * 根据URL获取缓存数据
     *
     * @param url 缓存使用的url，最终通过url的hashcode为CacheKey进行缓存
     * @return cache data 如果不存在或获取失败将返回 null
     */
    public static String getDataByCache(String url) {
        Entry entry = getCache(url);
        if (entry == null) {
            return null;
        }
        String data = null;
        try {
//			data = new String1(entry.data,HttpHeaderParser.parseCharset(entry.responseHeaders));
            data = new String(entry.data, HTTP.UTF_8);
        } catch (Exception e) {


        }
        return data;
    }

    /**
     * 根据URL获取缓存中的 ETag
     *
     * @param url 缓存使用的url，最终通过url的hashcode为CacheKey进行缓存
     * @return 如果不存在返回Null
     */
    public static String getETagByCache(String url) {
        Entry entry = getCache(url);
        if (entry == null) {
            return null;
        }
        return entry.etag;
    }

    /**
     * 根据URL获取缓存
     *
     * @param url 缓存使用的url，最终通过url的hashcode为CacheKey进行缓存
     * @return 如果不存在，将返回null
     */
    private static Entry getCache(String url) {
        Cache cache = REQUEST_QUEUE_DATA.getCache();
        return cache.get(url);
    }

    /**
     * 使缓存失效，失效并不意味这删除，Volley还会继续使用缓存的对象直到从服务器上获取到了新的数据，新的数据会覆盖旧的数据。
     *
     * @param url
     */
    public static void invalidateCache(String url) {
        REQUEST_QUEUE_DATA.getCache().invalidate(url, true);
    }

    /**
     * 删除指定缓存
     *
     * @param url
     */
    public static void removeCache(String url) {
        REQUEST_QUEUE_DATA.getCache().remove(url);
    }

    /**
     * 清除所有缓存
     */
    public static void removeAllCache(Context c) {
        deleteFilesByDirectory(c.getCacheDir());
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {
            deleteFilesByDirectory(c.getExternalCacheDir());
        }
    }

    public static String getCacheSize(Context c) {
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {
            return FileSizeUtil.getAutoFileOrFilesSize(c.getCacheDir().getAbsolutePath(), c.getExternalCacheDir().getAbsolutePath());
        }
        return FileSizeUtil.getAutoFileOrFilesSize(c.getCacheDir().getAbsolutePath());
    }

    private static void deleteFilesByDirectory(File directory) {
        if (directory != null && directory.exists() && directory.isDirectory()) {
            for (File item : directory.listFiles()) {
                if (item.isDirectory()) {
                    deleteFilesByDirectory(item);
                } else {
                    System.out.println(item.getAbsoluteFile() + "=====" + item.delete());
                }
            }
        }
    }

    /**
     * 获取ImageLoader对象，该对象可重用
     *
     * @return
     */
    public static ImageLoader getImageLoader() {
        return IMAGE_LOADER;
    }

    /**
     * 图片的 L1 缓存类
     */
    public static class BitmapLruCache extends LruCache<String, Bitmap> implements ImageCache {

        public BitmapLruCache(int maxSize) {
            super(maxSize);
        }

        @Override
        protected int sizeOf(String key, Bitmap value) {
            return value.getRowBytes() * value.getHeight();
        }

        @Override
        public Bitmap getBitmap(String url) {
            return get(url);
        }

        @Override
        public void putBitmap(String url, Bitmap bitmap) {
            put(url, bitmap);
        }
    }

    /**
     * 创建一个队列
     *
     * @param cache   磁盘缓存，如果为null，则会使用默认的磁盘缓存对象，大小为5M
     * @param stack
     * @param context
     * @return
     */
    public static RequestQueue newRequestQueue(DiskBasedCache cache, HttpStack stack, Context context) {
        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);

        String userAgent = "volley/0";
        try {
            String packageName = context.getPackageName();
            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
            userAgent = packageName + "/" + info.versionCode;
        } catch (NameNotFoundException e) {
        }

        if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

        Network network = new BasicNetwork(stack);
        if (cache == null) {
            cache = new DiskBasedCache(cacheDir);
        }

        RequestQueue queue = new RequestQueue(cache, network, context);
        queue.start();

        return queue;
    }
}
